package com.tinysand.advert.search.impl;

import com.tinysand.advert.index.DataTable;
import com.tinysand.advert.index.impl.*;
import com.tinysand.advert.index.support.AdUnitObject;
import com.tinysand.advert.index.support.AdvertObject;
import com.tinysand.advert.index.support.CommonStatus;
import com.tinysand.advert.search.Search;
import com.tinysand.advert.search.feature.DistrictFeature;
import com.tinysand.advert.search.feature.FeatureRelation;
import com.tinysand.advert.search.feature.InterestFeature;
import com.tinysand.advert.search.feature.KeywordFeature;
import com.tinysand.advert.search.media.AdSlot;
import com.tinysand.advert.search.vo.SearchRequest;
import com.tinysand.advert.search.vo.SearchResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author TINY
 */
@Service
public class SearchImpl implements Search {
    @Override
    public SearchResponse fetchAds(SearchRequest request) {
        List<AdSlot> adSlots = request
                .getRequestInfo().getAdSlots();

        KeywordFeature keywordFeature = request
                .getFeatureInfo().getKeywordFeature();
        DistrictFeature districtFeature = request
                .getFeatureInfo().getDistrictFeature();
        InterestFeature interestFeature = request
                .getFeatureInfo().getInterestFeature();

        FeatureRelation relation = request
                .getFeatureInfo().getRelation();

        SearchResponse response = new SearchResponse();
        Map<String, List<SearchResponse.Creative>> adSlot2Ads =
                response.getAdSlot2Ads();

        for (AdSlot adSlot : adSlots) {
            Set<Long> targetUnitIdSet;

            Set<Long> adUnitIdSet = DataTable.of(
                    AdUnitIndex.class
            ).match(adSlot.getPositionType());

            if (relation == FeatureRelation.AND) {
                filterKeywordFeature(adUnitIdSet, keywordFeature);
                filterDistrictFeature(adUnitIdSet, districtFeature);
                filterItTagFeature(adUnitIdSet, interestFeature);

                targetUnitIdSet = adUnitIdSet;
            } else {
                targetUnitIdSet = getOrRelationUnitIds(
                        adUnitIdSet, keywordFeature,
                        districtFeature, interestFeature
                );
            }

            List<AdUnitObject> unitObjects = DataTable.of(
                    AdUnitIndex.class).fetch(targetUnitIdSet);

            filterAdUnitAndPlanStatus(unitObjects, CommonStatus.VALID);

            List<Long> adIds = DataTable.of(AdUnitRelIndex.class)
                    .selectAds(unitObjects);

            List<AdvertObject> advertObjects = DataTable.of(
                    AdvertIndex.class).fetch(adIds);

            filterAdvertByAdSlot(
                    advertObjects, adSlot.getWidth(),
                    adSlot.getHeight(), adSlot.getTypes()
            );

            adSlot2Ads.put(adSlot.getAdSlotCode(),
                    buildCreativeResponse(advertObjects));
        }
        return response;
    }

    private List<SearchResponse.Creative> buildCreativeResponse(
            List<AdvertObject> advertObjects
    ) {
        if (CollectionUtils.isEmpty(advertObjects)) {
            return Collections.emptyList();
        }

        AdvertObject advertObject = advertObjects.get(
                Math.abs(new Random().nextInt() % advertObjects.size())
        );

        return Collections.singletonList(SearchResponse.convert(advertObject));
    }

    private void filterAdvertByAdSlot(List<AdvertObject> advertObjects,
                                      Integer width,
                                      Integer height,
                                      List<Integer> type) {
        if (CollectionUtils.isEmpty(advertObjects)) {
            return;
        }

        CollectionUtils.filter(advertObjects,
                advertObject -> advertObject.getAuditStatus().equals(
                        CommonStatus.VALID.getStatus())
                        && advertObject.getWidth().equals(width)
                        && advertObject.getHeight().equals(height)
                        && type.contains(advertObject.getType()));
    }

    private void filterAdUnitAndPlanStatus(List<AdUnitObject> unitObjects,
                                           CommonStatus status) {
        if (CollectionUtils.isEmpty(unitObjects)) {
            return;
        }

        CollectionUtils.filter(unitObjects,
                object -> object.getUnitStatus().equals(status.getStatus())
                        && object.getAdPlanObject().getPlanStatus().equals(
                        status.getStatus()));
    }

    private Set<Long> getOrRelationUnitIds(Set<Long> adUnitIdSet,
                                           KeywordFeature keywordFeature,
                                           DistrictFeature districtFeature,
                                           InterestFeature interestFeature) {
        if (CollectionUtils.isEmpty(adUnitIdSet)) {
            return Collections.emptySet();
        }
        Set<Long> keywordUnitIdSet = new HashSet<>();
        Set<Long> districtUnitIdSet = new HashSet<>();
        Set<Long> itUnitIdSet = new HashSet<>();

        filterKeywordFeature(keywordUnitIdSet, keywordFeature);
        filterDistrictFeature(districtUnitIdSet, districtFeature);
        filterItTagFeature(itUnitIdSet, interestFeature);

        return new HashSet<>(
                CollectionUtils.union(CollectionUtils.union(
                        keywordUnitIdSet, districtUnitIdSet), itUnitIdSet)
        );
    }

    private void filterKeywordFeature(Collection<Long> adUnitIds,
                                      KeywordFeature keywordFeature) {
        if (CollectionUtils.isEmpty(adUnitIds)) {
            return;
        }
        if (CollectionUtils.isNotEmpty(keywordFeature.getKeywords())) {
            CollectionUtils.filter(adUnitIds, adUnitId ->
                    DataTable.of(KeywordIndex.class)
                            .match(adUnitId, keywordFeature.getKeywords()));
        }
    }

    private void filterDistrictFeature(Collection<Long> adUnitIds,
                                      DistrictFeature districtFeature) {
        if (CollectionUtils.isEmpty(adUnitIds)) {
            return;
        }
        if (CollectionUtils.isNotEmpty(districtFeature.getDistricts())) {
            CollectionUtils.filter(adUnitIds, adUnitId ->
                    DataTable.of(DistrictIndex.class)
                            .match(adUnitId, districtFeature.getDistricts()));
        }
    }

    private void filterItTagFeature(Collection<Long> adUnitIds,
                                    InterestFeature interestFeature) {
        if (CollectionUtils.isEmpty(adUnitIds)) {
            return;
        }
        if (CollectionUtils.isNotEmpty(interestFeature.getIts())) {
            CollectionUtils.filter(adUnitIds, adUnitId ->
                    DataTable.of(InterestIndex.class)
                            .match(adUnitId, interestFeature.getIts()));
        }
    }
}
